package edu.hubu.service.impl;

import edu.hubu.authentication.CustomAuthenticationProvider;
import edu.hubu.commons.JwtUils;
import edu.hubu.commons.Object2Map;
import edu.hubu.dao.impl.IUserDao;
import edu.hubu.exception.*;
import edu.hubu.model.Permission;
import edu.hubu.model.Role;
import edu.hubu.model.Token;
import edu.hubu.model.User;
import net.moon.commons.ObjectUtils;
import net.moon.commons.ResultsJson;
import net.moon.commons.StatusCode;
import net.moon.jpa.service.DefaultService;
import org.slf4j.Logger;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Service;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * @author moonlan
 * @date 2020/10/20 下午2:27
 */
@Service
public class UserService extends DefaultService<User, String> implements UserDetailsService {
    private static final Logger log = org.slf4j.LoggerFactory.getLogger(UserService.class);
    private final BCryptPasswordEncoder encoder;

    private static AuthenticationManager authManager;

    private static JwtUils jwtUils;


    public UserService(IUserDao dao, BCryptPasswordEncoder encoder, DefaultService<Token, String> tokenService) {
        super(dao, User.class, "userIsDeleted", "userIsForbidden");
        this.encoder = encoder;
        authManager = new CustomAuthenticationProvider(this, encoder);
        jwtUils = new JwtUils(this, tokenService);
    }

    public ResultsJson login(String userId, String password, String checkCode, HttpServletRequest request) {
        if (userId == null || password == null || checkCode == null) {
            throw new WrongUsernameOrPasswordException("用户名或密码错误");
        }
        User user = findEntityById(userId);
        ServletContext context = request.getServletContext();
        //获取服务器验证码
        String checkcodeServer = "";
        try {
            checkcodeServer = context.getAttribute("CHECKCODE_SERVER").toString();
        } catch (Exception e) {
            throw new ClientException("请刷新验证码");
        }

        //获取前端验证码
        if (!checkcodeServer.equalsIgnoreCase(checkCode)) {
            throw new SyntaxErrorException("验证码错误");
        }

        if (user.isUserIsDeleted() || user.isUserIsForbidden()) {
            context.removeAttribute("CHECKCODE_SERVER");
            throw new PermissionDeniedException("用户不存在");
        }

        if (!encoder.matches(password, user.getUserPassword())) {
            context.removeAttribute("CHECKCODE_SERVER");
            throw new WrongUsernameOrPasswordException("用户名或密码错误");
        } else {
            context.removeAttribute("CHECKCODE_SERVER");
            //生成token
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(userId, password);
            Authentication authenticate = authManager.authenticate(authRequest);
            SecurityContextHolder.getContext().setAuthentication(authenticate);
            String token = jwtUils.getToken(userId);
            HashMap<String, Object> map = new HashMap<>();
            map.put("token", token);
            map.put("user", Object2Map.filterMap(user));
            return ResultsJson.toJson(StatusCode.SUCCESS, 1, 1, map);
        }
    }

    public ResultsJson logout(HttpServletRequest request, HttpServletResponse response, String token) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {
            User one = findEntityById(auth.getName());
            one.getToken().setTokenIsDeleted(true);
            updateEntityById(one.getUserSid(), one);
            new SecurityContextLogoutHandler().logout(request, response, auth);
            return ResultsJson.toJson(StatusCode.SUCCESS, 1, 1, true);
        }
        throw new InternalServerException();
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = this.getEntityById(s);
        if (user == null) {
            log.error("未找到该用户信息: {}", s);
            throw new UsernameNotFoundException("未找到该用户信息:" + s);
        }
        HashSet<GrantedAuthority> authorities = new HashSet<>();
        if (!user.isUserIsForbidden() && !user.isUserIsDeleted()) {
            Role role = user.getRole();
            if (ObjectUtils.Companion.isNullOrEmpty(role)) {
                throw new PermissionDeniedException("权限不足");
            }

            if (!role.isRoleIsDeleted() && !role.isRoleIsForbidden()) {
                List<Permission> permissions = role.getPermissions();
                for (Permission permission : permissions) {
                    if (!permission.isPermissionIsDeleted() && !permission.isPermissionIsForbidden()) {
                        SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission.getPermissionName());
                        authorities.add(authority);
                    }
                }
            }
        } else {
            throw new PermissionDeniedException("用户不存在");
        }
        return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getUserPassword(), authorities);
    }

    public ResultsJson editPwdById(String id, String oldPassword, String newPassword) {
        User byId = this.findEntityById(id);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String encodedNewPwd = new BCryptPasswordEncoder().encode(newPassword);
        if (!encoder.matches(oldPassword, byId.getUserPassword())) {
            return ResultsJson.toJson(StatusCode.SUCCESS, 1, 0, false);
        }
        byId.setUserPassword(encodedNewPwd);
        return ResultsJson.toJson(StatusCode.SUCCESS, 1, 0, this.updateEntityById(id, byId));
    }
}
